#pragma once
#include <iostream>
#include <ctime>
#include <atomic>
#include <sys/stat.h>
#include <sys/types.h>
#include <sqlite3.h>
#include <fstream>
#include <sstream>

#include <mysql/mysql.h>
#include <unordered_map>
#include <string>

#include <random>
#include <chrono>
#include <iomanip>

namespace ns_helper
{
    class DateHelper
    {
    public:
        static time_t now()
        {
            return time(nullptr);
        }
    };

    class FileHelper
    {
    public:
        static bool exists(const std::string &pathname)
        {
            struct stat st;
            // stat遵循POSIX标准（Portable Operating System Interface for UNIX 可移植操作系统接口）
            // 具有良好的跨平台移植性
            return stat(pathname.c_str(), &st) >= 0; // 第二个参数不能给nullptr，因为它是一个输出型参数
        }
        static std::string getPath(const std::string &pathname)
        {
            // ./a/b/c/d
            // 从后往前找第一个'/' 然后返回./a/b/c
            // 如果没有，则返回.
            // find_last_of
            size_t pos = pathname.find_last_of(R"(/\)"); // 支持(/)Linux、Mac OS和Win(\)
            if (pos == std::string::npos)
            {
                return ".";
            }
            return pathname.substr(0, pos);
        }
        static bool createDir(const std::string &pathname)
        {
            // ./a/b/c
            // 从前往后依次创建 ./a  ./a/b  ./a/b/c
            size_t pos = pathname.find_first_of(R"(/\)");
            while (pos != std::string::npos)
            {
                umask(0);
                std::string substr = pathname.substr(0, pos);
                int ret = mkdir(substr.c_str(), 0775);
                if (ret == -1 && errno != EEXIST)
                {
                    std::cout << "目录创建失，总目录：" << pathname << "，当前目录：" << substr << "\n";
                    return false;
                }
                pos = pathname.find_first_of(R"(/\)", pos + 1);
            }
            // 创建整个目录
            return (mkdir(pathname.c_str(), 0775) != -1 || errno == EEXIST);
        }

        static bool createFile(const std::string &filename)
        {
            // 1. 先看该文件是否存在
            if (exists(filename))
                return true;
            // 2. 写方式打开（即创建）
            // 这里以追加写打开，防止多线程重入该函数导致意想不到的bug，提高代码健壮性
            std::ofstream ofs(filename, std::ios::app);
            if (!ofs.is_open())
            {
                std::cout << "创建文件失败，filename: " << filename << "\n";
                return false;
            }
            ofs.close();
            return true;
        }
    };

    class SqliteHelper
    {
    public:
        using SqliteCallback = int (*)(void *, int, char **, char **);
        SqliteHelper(const std::string &dbfile)
            : _dbfile(dbfile), _handler(nullptr) {}

        ~SqliteHelper()
        {
            close();
        }

        bool open()
        {
            if (sqlite3_open_v2(_dbfile.c_str(), &_handler, SQLITE_OPEN_CREATE | SQLITE_OPEN_READWRITE, nullptr) != SQLITE_OK)
            {
                std::cout << "打开数据库失败 " << errmsg() << " _dbfile：" << _dbfile << "\n";
                return false;
            }
            return true;
        }

        void close()
        {
            if (_handler != nullptr)
            {
                sqlite3_close_v2(_handler);
                _handler = nullptr;
            }
        }

        bool begin()
        {
            if (sqlite3_exec(_handler, "begin;", nullptr, nullptr, nullptr) != SQLITE_OK)
            {
                std::cout << "SQLite3开启事务失败，" << errmsg() << "\n";
                return false;
            }
            return true;
        }

        bool exec(const std::string &sql, SqliteCallback cb, void *arg)
        {
            if (sqlite3_exec(_handler, sql.c_str(), cb, arg, nullptr) != SQLITE_OK)
            {
                std::cout << "执行sql语句：" << sql << " 失败，" << errmsg() << std::endl;
                return false;
            }
            return true;
        }

        bool commit()
        {
            if (sqlite3_exec(_handler, "commit;", nullptr, nullptr, nullptr) != SQLITE_OK)
            {
                std::cout << "SQLite3提交事务失败，" << errmsg() << "\n";
                return false;
            }
            return true;
        }

        bool savePoint(const std::string &point)
        {
            std::string savepoint_sql = "savepoint " + point + ";";
            if (sqlite3_exec(_handler, savepoint_sql.c_str(), nullptr, nullptr, nullptr) != SQLITE_OK)
            {
                std::cout << "SQLite3 设置事务保存点失败：" << errmsg() << "\n";
                return false;
            }
            return true;
        }

        bool rollback(const std::string &point = "")
        {
            std::string rollback_sql = "rollback";
            if (!point.empty())
            {
                rollback_sql += " to " + point + ";";
            }
            if (sqlite3_exec(_handler, rollback_sql.c_str(), nullptr, nullptr, nullptr) != SQLITE_OK)
            {
                std::cout << "SQLite3 事务回滚失败：" << errmsg() << "\n";
                return false;
            }
            return true;
        }

        std::string errmsg()
        {
            if (_handler != nullptr)
                return sqlite3_errmsg(_handler);
            else
                return "sqlite3句柄为空";
        }

    private:
        std::string _dbfile;
        sqlite3 *_handler;
    };

    /*
    MYSQL *mysql_real_connect(MYSQL *mysql, const char *host,
                            const char *user,
                            const char *passwd,
                            const char *db,
                            unsigned int port,
                            const char *unix_socket,
                            unsigned long clientflag);
    */

    class MySQLHelper
    {
    public:
        MySQLHelper(const std::string &conf_file) : _handler(mysql_init(nullptr)), _conf_file(conf_file) {}

        bool begin()
        {
            if (mysql_query(_handler, "begin;") != 0)
            {
                std::cout << "mysql开启事务失败，" << errmsg() << "\n";
                return false;
            }
            return true;
        }

        bool commit()
        {
            if (mysql_query(_handler, "commit;") != 0)
            {
                std::cout << "mysql提交事务失败，" << errmsg() << "\n";
                return false;
            }
            return true;
        }

        bool savePoint(const std::string &point)
        {
            std::string savepoint_sql = "savepoint " + point + ";";
            if (mysql_query(_handler, savepoint_sql.c_str()) != 0)
            {
                std::cout << "MySQL 设置事务保存点失败：" << errmsg() << "\n";
                return false;
            }
            return true;
        }

        bool rollback(const std::string &point = "")
        {
            std::string rollback_sql = "rollback";
            if (!point.empty())
            {
                rollback_sql += " to " + point + ";";
            }
            if (mysql_query(_handler, rollback_sql.c_str()) != 0)
            {
                std::cout << "MySQL 事务回滚失败：" << errmsg() << "\n";
                return false;
            }
            return true;
        }

        bool open()
        {
            // 1. 读取配置文件，拿到host、user、passwd、db、port
            if (!load())
            {
                mysql_close(_handler);
                _handler = nullptr;
                return false;
            }
            // 2. 连接MySQL数据库
            _handler = mysql_real_connect(_handler, _conf_map["host"].c_str(), _conf_map["user"].c_str(),
                                          _conf_map["passwd"].c_str(), _conf_map["db"].c_str(), std::stoi(_conf_map["port"]), nullptr, 0);

            if (_handler == nullptr)
            {
                std::cout << "MySQL连接失败, 原因: " << mysql_error(_handler) << "\n";
                mysql_close(_handler);
                _handler = nullptr;
                return false;
            }
            return true;
        }

        ~MySQLHelper()
        {
            close();
        }

        void close()
        {
            if (_handler != nullptr)
            {
                mysql_close(_handler);
                _handler = nullptr;
            }
        }

        bool exec(const std::string &sql)
        {
            if (0 != mysql_query(_handler, sql.c_str()))
            {
                std::cout << "sql语句执行失败: " << sql << " ,原因: " << errmsg() << std::endl;
                return false;
            }
            return true;
        }

        std::string errmsg()
        {
            if (_handler != nullptr)
                return mysql_error(_handler);
            else
                return "mysql句柄为空";
        }

    private:
        bool load()
        {
            std::ifstream ifs(_conf_file);
            std::string line;
            while (std::getline(ifs, line))
            {
                size_t pos = line.find(':');
                if (pos == std::string::npos)
                {
                    std::cout << "MySQL配置文件解析失败,某一行不符合规范: " << line << "\n";
                    return false;
                }
                _conf_map[line.substr(0, pos)] = line.substr(pos + 1);
            }
            return true;
        }

        MYSQL *_handler;
        std::string _conf_file;
        std::unordered_map<std::string, std::string> _conf_map;
    };


     class UuidHelper
    {
    public:
        static std::string uuid()
        {
            std::random_device rd; // 机器随机数,效率太低,因此我们用梅森旋转算法
            // std::cout<<rd()<<"\n";
            std::mt19937_64 generator(rd());
            // std::cout<<generator()<<"\n";//但是这个数太大了,我们只想生成0~255之间的随机数啊
            std::uniform_int_distribution<int> distribute(0, 255);
            // std::cout<<distribute(generator)<<"\n";

            // 生成8个0~255之间的随机数,每个搞成16进制来表示,而且我们要设置位宽和填充,以及中间的分隔符
            std::ostringstream oss;
            for (int i = 0; i < 8; i++)
            {
                oss << std::setw(2) << std::setfill('0') << std::hex << distribute(generator);
                if (i == 3 || i == 5 || i == 7)
                {
                    oss << "-";
                }
            }
            // 搞一个原子类的占8个字节的整形 -> 64位平台下size_t正好8个字节,32位平台下却只有4个字节,所有不妨直接用uint64_t
            static std::atomic<uint64_t> seq(1); // 搞成静态懒汉
            
            uint64_t num = seq.fetch_add(1);
            for (int i = 7; i >= 0; i--)
            {
                // 从后往前依次取一个字节转成16进制进行填充
                oss << std::setw(2) << std::setfill('0') << std::hex << ((num << i * 8) & 0xff);
                if (i == 6)
                {
                    oss << "-";
                }
            }
            return oss.str();
        }
    };

}
